﻿using System;
using System.IO;
using System.Net;
using System.Timers;
using HashFoo.Core;

namespace HashFoo.Rest.CommonTests.MockResources
{
    /*
     * ReSharper seems to have an issue with .NET types that come out of the portable library, so analysis has been turned off for this file.
     * DMF 2011-09-22
     */

    public class WebResultStringMock : IWebResultString
    {
        public WebResultStringMock(string data, HttpStatusCode status)
        {
            Data = data;
            Details = new WebResultDetails { StatusCode = status };
        }

        /// <summary>
        /// The consumable data returned in the response.
        /// </summary>
        public string Data { get; set; }

        /// <summary>
        /// The response meta data details.
        /// </summary>
        public WebResultDetails Details { get; set; }
    }

    public class WebClientMock : IWebClient
    {
        bool _useCompletedCallback;
        bool _useErrorCallback;

        public WebClientMock()
        {
            DefaultHeaders = new WebHeaderCollection();
            UseCompletedCallback();
            CallbackWait = TimeSpan.FromMilliseconds(200);

            ResultGenerator = s => new WebResultStringMock(s, HttpStatusCode.OK);
            ErrorGenerator = () => new WebRequestError("mock error", new Exception("mock exception"));
        }

        public TimeSpan CallbackWait { get; set; }

        public WebHeaderCollection DefaultHeaders { get; private set; }

        /// <summary>
        /// Holds the cookies for the current web client.  Cookies are kept between requests for each client.
        /// </summary>
        public CookieContainer Cookies
        {
            get { throw new NotImplementedException(); }
        }

        public Func<WebRequestError> ErrorGenerator { get; set; }
        public Func<string, IWebResultString> ResultGenerator { get; set; }

        public IWebClientOperation DownloadStreamAsync(Uri uri, Action<IWebResultStream> successCallback, Action<WebRequestError> errorCallback = null, Action cancelCallback = null, Action<ProgressEventArgs> progressCallback = null, string method = null, bool buffer = true)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Performs a web request that uploads data from a byte array.  Any server response is returned as a string.
        /// </summary>
        /// <param name="uri">The uri to call</param><param name="data">The data to upload</param><param name="successCallback">Callback for successful completion</param><param name="errorCallback">Callback if there is an error</param><param name="cancelCallback">Callback if the operation is canceled</param><param name="progressCallback">Callback for progress updates of the upload</param><param name="method">HTTP method override.</param>
        /// <returns>
        /// The operation, useful to cancel if needed.
        /// </returns>
        public IWebClientOperation UploadBytesAsync(Uri uri, byte[] data, Action<IWebResultString> successCallback = null, Action<WebRequestError> errorCallback = null, Action cancelCallback = null, Action<ProgressEventArgs> progressCallback = null, string method = null)
        {
            throw new NotImplementedException();
        }

        public IWebClientOperation DownloadStringAsync(Uri uri, Action<IWebResultString> completedCallback, Action<WebRequestError> errorCallback = null, Action cancelCallback = null, Action<ProgressEventArgs> progressCallback = null, string method = null)
        {
            return ExecuteWebOperation(completedCallback, errorCallback, cancelCallback);
        }

        public IWebClientOperation UploadStringAsync(Uri uri, string data, Action<IWebResultString> completedCallback = null, Action<WebRequestError> errorCallback = null, Action cancelCallback = null, string method = null)
        {
            return ExecuteWebOperation(completedCallback, errorCallback, cancelCallback, data);
        }

        /// <summary>
        /// Performs a web request that uploads data from a stream.  Any server response is returned as a string.
        /// </summary>
        /// <param name="uri">The uri to call</param><param name="data">The data to upload</param><param name="successCallback">Callback for successful completion</param><param name="errorCallback">Callback if there is an error</param><param name="cancelCallback">Callback if the operation is canceled</param><param name="progressCallback">Callback for progress updates of the upload</param><param name="method">HTTP method override.</param>
        /// <returns>
        /// The operation, useful to cancel if needed.
        /// </returns>
        public IWebClientOperation UploadStreamAsync(Uri uri, Stream data, Action<IWebResultString> successCallback = null, Action<WebRequestError> errorCallback = null, Action cancelCallback = null, Action<ProgressEventArgs> progressCallback = null, string method = null)
        {
            throw new NotImplementedException();
        }

        public WebClientMock UseCompletedCallback()
        {
            _useCompletedCallback = true;
            _useErrorCallback = false;

            return this;
        }

        public WebClientMock UseErrorCallback()
        {
            _useErrorCallback = true;
            _useCompletedCallback = false;

            return this;
        }

        IWebClientOperation ExecuteWebOperation(Action<IWebResultString> completedCallback, Action<WebRequestError> errorCallback = null, Action cancelCallback = null, string data = null)
        {
            var worker = new Timer(CallbackWait.TotalMilliseconds);
            var op = new WebClientOperationMock(worker);
            op.OnCanceled += (sender, args) => op.MarkAsComplete(cancelCallback);

            worker.Elapsed += (sender, args) =>
            {
                if (_useCompletedCallback || _useErrorCallback == false) op.MarkAsComplete(() => completedCallback(ResultGenerator(data)));
                else if (errorCallback != null) op.MarkAsComplete(() => errorCallback(ErrorGenerator()));
            };

            worker.Start();
            return op;
        }
    }
}